package sample.utils;

import javafx.beans.binding.DoubleExpression;

public class MatrixUtils {
    private static void minFromRow(double[][] matrix, int r, int c, double[] rmin) {
        for (int i = 0; i < r; i++) {
            double min = matrix[i][0];
            for (int j = 0; j < c; j++) {
                min = Math.min(min, matrix[i][j]);
            }
            rmin[i] = min;
        }
    }

    private static void minFromColumn(double[][] matrix, int r, int c, double[] cmin) {
        for (int i = 0; i < c; i++) {
            double min = matrix[0][i];
            for (int j = 0; j < r; j++) {
                min = Math.min(min, matrix[j][i]);
            }
            cmin[i] = min;
        }
    }

    private static void maxFromColumn(double[][] matrix, int r, int c, double[] cmax) {
        for (int i = 0; i < c; i++) {
            double max = matrix[0][i];
            for (int j = 0; j < r; j++) {
                max = Math.max(max, matrix[j][i]);
            }
            cmax[i] = max;
        }
    }

    private static void maxFromRow(double[][] matrix, int r, int c, double[] rmax) {
        for (int i = 0; i < r; i++) {
            double max = matrix[i][0];
            for (int j = 0; j < c; j++) {
                max = Math.max(max, matrix[i][j]);
            }
            rmax[i] = max;
        }
    }

    public static double[] calcMaxOnColumn(final double[][] array, final int r, final int c) {
        double[] maxArray = new double[c];
        double maximum = 0;
        for (int i = 0; i < c; i++) {
            maximum = array[0][i];
            for (int j = 0; j < r; j++) {
                maximum = Math.max(maximum, array[j][i]);
            }
            maxArray[i] = maximum;
        }
        return maxArray;
    }

    public static double[] calcMinOnColumn(final double[][] array, final int r, final int c) {
        double[] minArray = new double[c];
        double minimum = 0;
        for (int i = 0; i < c; i++) {
            minimum = array[0][i];
            for (int j = 0; j < r; j++) {
                minimum = Math.min(minimum, array[j][i]);
            }
            minArray[i] = minimum;
        }
        return minArray;
    }

    public static Double getOptimist(final double[][] matrix, final int r, final int c) {
        double[] rmax = new double[r];
        maxFromRow(matrix, r, c, rmax);
        double maximax = rmax[0];
        for (double v : rmax) {
            maximax = Math.max(maximax, v);
        }
        return maximax;
    }

    public static Double getPessimist(final double[][] matrix, final int r, final int c) {
        double[] rmin = new double[r];
        minFromRow(matrix, r, c, rmin);
        double minmax = rmin[0];
        for (double v : rmin) {
            minmax = Math.max(minmax, v);
        }
        return minmax;
    }


    public static Double getRealist(final double[][] matrix, final int r, final int c, final double alpha) {
        double[] rmax = new double[r];
        double[] rmin = new double[r];
        double[] result = new double[r];
        double maxR;
        maxFromRow(matrix, r, c, rmax);
        minFromRow(matrix, r, c, rmin);
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                result[i] = (alpha * rmax[i]) + ((1 - alpha) * rmin[i]);
            }
        }
        maxR = result[0];
        for (double v : result) {
            maxR = Math.max(maxR, v);
        }
        return maxR;
    }

    public static Double getRegret(final double[][] matrix, final int r, final int c) {
        double[] cmax = new double[c];
        double[][] substract = new double[r][c];
        double[] smax = new double[r];
        maxFromColumn(matrix, r, c, cmax);
        for (int i = 0; i < c; i++) {
            for (int j = 0; j < r; j++) {
                substract[j][i] = cmax[i] - matrix[j][i];
            }
        }
        maxFromRow(substract, r, c, smax);
        double min = smax[0];
        for (double v : smax) {
            min = Math.min(min, v);
        }
        return min;
    }

    public static Double getEchiprob(final double[][] matrix, final int r, final int c) {
        double[] sum = new double[r];
        double coefficient = (double) 1 / c;
        for (int i = 0; i < r; i++) {
            sum[i] = 0;
            for (int j = 0; j < c; j++) {
                sum[i] = sum[i] + matrix[i][j];
            }
            sum[i] *= coefficient;
        }
        double max = sum[0];
        for (double v : sum) {
            max = Math.max(max, v);
        }
        return max;
    }

    public static Double getBayes(final double[][] matrix, final double[] probability, final int r, final int c) {
        double[] sum = new double[r];
        for (int i = 0; i < r; i++) {
            double localProd = 0;
            double rsum = 0;
            for (int j = 0; j < c; j++) {
                localProd = matrix[i][j] * probability[j];
                rsum += localProd;
            }
            sum[i] = rsum;
        }
//        Calc max from sum[i]
        double result = sum[0];
        for (double v : sum) {
            result = Math.max(result, v);
        }
        return result;
    }

    public static Double getBernoulli(final double[][] matrix, final int r, final int c) {
        double[] sum = new double[r];
        double[] prod = new double[r];
        double preSum = 0;
        //        Calc probability
        double probability = (double) 1 / c;
        System.out.println("Probabilitatea pentru matrice este : " + probability);
//        Calc Sum
        for (int i = 0; i < r; i++) {
            preSum = 0;
            for (int j = 0; j < c; j++) {
                preSum += matrix[i][j];
            }
            sum[i] = preSum;
        }
//        Print Sum
        System.out.println("Suma :");
        for (double s : sum) {
            System.out.print("\t" + s);
        }
        System.out.println();
//        Calc prod
        for (int i = 0; i < sum.length; i++) {
            prod[i] = sum[i] * probability;
        }
//        Calc max from prod
        double max = prod[0];
        for (int i = 0; i < prod.length; i++) {
            max = Math.max(max, prod[i]);
        }
        return max;
    }

    public static Double getAditive(final double[][] matrix, final double[] priority, final int r, final int c) {
        double[][] utilityMatrix = new double[r][c];
        double[] utilitySum = new double[r];
        double[] maxValuesOnColumn = new double[c];
        double[] minValuesOnColumn = new double[c];
        maxValuesOnColumn = calcMaxOnColumn(matrix, r, c); // calc maximum from column
        minValuesOnColumn = calcMinOnColumn(matrix, r, c); // calc minimum from column
//        calc utility matrix
        utilityMatrix = calcUtilityMatrix(matrix, maxValuesOnColumn, minValuesOnColumn, priority, r, c);
//        calc sum for utility matrix (each row)
        utilitySum = calcSumForRowBidimensional(utilityMatrix, r, c);
//        calc max from utilityMax array
        double finalValue = calcMaxForRow(utilitySum, r);
        System.out.println("Decizia optima : " + finalValue);
        return finalValue;
    }

    public static Double getGlobalUtility(final double[][] matrix, final int r, final int c, final double[] priority, final double[] probability) {
        double[] maxValuesOnColumn = new double[c];
        double[] minValuesOnColumn = new double[c];
        double[][] utilityMatrix = new double[r][c];
        double[][] globalUtilityMatrix = new double[r][c];
        double[] utilityGlobalSum = new double[r];
        maxValuesOnColumn = calcMaxOnColumn(matrix, r, c);
        minValuesOnColumn = calcMinOnColumn(matrix, r, c);
        utilityMatrix = calcUtilityMatrix(matrix, maxValuesOnColumn, minValuesOnColumn, priority, r, c);
        calcGlobalUtility(r, c, probability, utilityMatrix, globalUtilityMatrix);
        utilityGlobalSum = calcSumForRowBidimensional(globalUtilityMatrix, r, c);
        double finalValue = calcMaxForRow(utilityGlobalSum, r);
        return finalValue;
    }


    private static void calcGlobalUtility(int r, int c, double[] probability, double[][] utilityMatrix, double[][] globalUtilityMatrix) {
        //        calc global utility
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                globalUtilityMatrix[i][j] = utilityMatrix[i][j] * probability[j];
            }
        }
    }

    public static double calcMaxForRow(final double[] array, final int r) {
        double max = array[0];
        for (int i = 0; i < r; i++) {
            max = Math.max(max, array[i]);
        }
        return max;
    }

    private static double[] calcSumForRowBidimensional(final double[][] array, final int r, final int c) {
        double[] sumArray = new double[r];
        double sum;
        for (int i = 0; i < r; i++) {
            sum = 0;
            for (int j = 0; j < c; j++) {
                sum += array[i][j];
            }
            sumArray[i] = sum;
        }
        return sumArray;
    }


    public static double[][] getUtility(final double[][] matrix, final int r, final int c, final double[] priority) {
        double[][] utilityMatrix;
        double[] maxValuesOnColumn = new double[c];
        double[] minValuesOnColumn = new double[c];
        maxFromColumn(matrix, r, c, maxValuesOnColumn);
        minFromColumn(matrix, r, c, minValuesOnColumn);
        utilityMatrix = calcUtilityMatrix(matrix, maxValuesOnColumn, minValuesOnColumn, priority, r, c);
        return utilityMatrix;
    }


    private static double[][] calcUtilityMatrix(final double[][] main, final double[] maxValuesOnColumn
            , final double[] minValuesOnColumn, final double[] priority, final int r, final int c) {
        double[][] utility = new double[r][c];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (priority[j] == 0) {
                    utility[i][j] = (maxValuesOnColumn[j] - main[i][j]) / (maxValuesOnColumn[j] - minValuesOnColumn[j]);
                } else if (priority[j] == 1) {
                    utility[i][j] = (main[i][j] - minValuesOnColumn[j]) / (maxValuesOnColumn[j] - minValuesOnColumn[j]);
                }
            }
        }
        return utility;
    }

    public static double[][] calcGrDepartare(final double[][] mainMatrix, final double priority, final int r,
                                             final int c) {
        double[][] distance = new double[r][c];
        double[] maxValuesOnColumn = calcMaxOnColumn(mainMatrix, r, c);
        double[] minValuesOnColumn = calcMinOnColumn(mainMatrix, r, c);

        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (priority == 0) {
                    distance[i][j] = (mainMatrix[i][j] - minValuesOnColumn[j]) / mainMatrix[i][j];
                } else if (priority == 1) {
                    distance[i][j] = (maxValuesOnColumn[j] - mainMatrix[i][j]) / maxValuesOnColumn[j];
                }
            }
        }
        return distance;
    }


    public static double[][] calcXMatrix(final double[][] distanceMatrix, final double[] alpha, final int r,
                                         final int c) {
        double[][] xMatrix = new double[r][c];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                xMatrix[i][j] = distanceMatrix[i][j] * alpha[j];
            }
        }
        return xMatrix;
    }

    public static double[][] calcBelogingMatrix(final double[][] xMatrix, final double exponent, final double priority,
                                                final int r, final int c) {
        double[][] belogingMatrix = new double[r][c];
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                if (priority == 0) {
                    belogingMatrix[i][j] = Math.pow(exponent, xMatrix[i][j]);
                } else if (priority == 1) {
                    belogingMatrix[i][j] = Math.pow(exponent, -xMatrix[i][j]);
                }
            }
        }
        return belogingMatrix;
    }
}
